package org.xteam.box2text.test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.xteam.box2text.BoxToText;
import org.xteam.box2text.Text;
import org.xteam.box2text.box.AOperator;
import org.xteam.box2text.box.Align;
import org.xteam.box2text.box.AlignmentOption;
import org.xteam.box2text.box.Box;
import org.xteam.box2text.box.BoxFactory;
import org.xteam.box2text.box.BoxPackage;
import org.xteam.box2text.box.GOperator;
import org.xteam.box2text.box.HOperator;
import org.xteam.box2text.box.IOperator;
import org.xteam.box2text.box.OperatorKinds;
import org.xteam.box2text.box.Option;
import org.xteam.box2text.box.SLOperator;
import org.xteam.box2text.box.TextBox;
import org.xteam.box2text.box.VOperator;
import org.xteam.pico.model.pico.Declaration;
import org.xteam.pico.model.pico.PicoFactory;
import org.xteam.pico.model.pico.Program;
import org.xteam.pico.model.pico.impl.PicoPackageImpl;
import org.xteam.pico.model.pico.util.PicoSwitch;

public class PicoToBox extends PicoSwitch<Box> {
	
	public <T extends EObject> List<Box> doListSeparatedBy(List<T> l,
			String separator) {
		List<Box> res = new ArrayList<Box>();
		boolean isFirst = true;
		for (T e : l) {
			if (!isFirst)
				res.add(createTextBox(separator));
			isFirst = false;
			res.add(doSwitch(e));
		}
		return res;
	}

	public <T extends EObject> List<Box> doList(List<T> l) {
		List<Box> res = new ArrayList<Box>();
		for (T e : l) {
			res.add(doSwitch(e));
		}
		return res;
	}

	public Box caseProgram(Program program) {
		List<Box> idTypes = doListSeparatedBy(program.getDeclarations(), ",");
		Box decls = createVOperator(createTextBox("declare"), createIOperator(
				createHOperator(0, createAOperator(
						new Align[] { Align.R, Align.L, Align.L, Align.L },
						createSLOperator(OperatorKinds.R, idTypes)), createTextBox(";"))));
		List<Box> statements = doList(program.getStatements());
		return createVOperator(createTextBox("begin"), createVOperator(2, createIOperator(decls)// ,
				// new IOperator(new VOperator(
				// new SLOperator(OperatorKinds.H, /* hs = */0, statements)))
				), createTextBox("end"));
	}

	public Box caseDeclaration(Declaration decl) {
		return createGOperator(createTextBox(decl.getId()), createTextBox(":"),
				createTextBox(decl.getType()));
	}
	
	private Box createIOperator(Box...boxes) {
		IOperator b = BoxFactory.eINSTANCE.createIOperator();
		b.getArguments().addAll(Arrays.asList(boxes));
		return b;
	}
	
	private Box createVOperator(Box...boxes) {
		return createVOperator(0, boxes);
	}
	
	private Box createVOperator(int vs, Box...boxes) {
		VOperator b = BoxFactory.eINSTANCE.createVOperator();
		b.getOptions().add(createOption("vs", vs));
		b.getArguments().addAll(Arrays.asList(boxes));
		return b;
	}
	
	private Option createOption(String name, int value) {
		Option b = BoxFactory.eINSTANCE.createOption();
		b.setName(name);
		b.setValue(value);
		return null;
	}

	private Box createHOperator(int hs, Box...boxes) {
		HOperator b = BoxFactory.eINSTANCE.createHOperator();
		b.getOptions().add(createOption("hs", hs));
		b.getArguments().addAll(Arrays.asList(boxes));
		return b;
	}
	
	private Box createAOperator(Align[] aligns, Box...boxes) {
		AOperator b = BoxFactory.eINSTANCE.createAOperator();
		for (Align a : aligns) {
			AlignmentOption ao = BoxFactory.eINSTANCE.createAlignmentOption();
			ao.setType(a);
			b.getAlignmentOptions().add(ao);
		}
		b.getArguments().addAll(Arrays.asList(boxes));
		return b;
	}
	
	private Box createSLOperator(OperatorKinds op, List<Box> boxes) {
		SLOperator b = BoxFactory.eINSTANCE.createSLOperator();
		b.setOperator(op);
		b.getArguments().addAll(boxes);
		return b;
	}

	private Box createGOperator(Box...boxes) {
		GOperator b = BoxFactory.eINSTANCE.createGOperator();
		b.getArguments().addAll(Arrays.asList(boxes));
		return b;
	}

	private TextBox createTextBox(String value) {
		TextBox b = BoxFactory.eINSTANCE.createTextBox();
		b.setContents(value);
		return b;
	}

	public static void main(String[] args) throws IOException {
		Program prog = createProgram();
		PicoToBox p2b = new PicoToBox();
		Box box = p2b.doSwitch(prog);
		BoxToText b2t = new BoxToText();
		Text text = b2t.text(box);
		System.out.println(text);
		save(Arrays.asList(box), "models/box2.xmi");
	}
	
	private static void save(List<Box> boxes, String filename) throws IOException {
		Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("xmi", new XMIResourceFactoryImpl());

		ResourceSet rs = new ResourceSetImpl();
		Resource res = rs.createResource(URI.createFileURI(filename));
		res.getContents().addAll(boxes);
		
		BoxPackage.eINSTANCE.eResource().setURI(URI.createFileURI("box.ecore"));
		Map<String, Object> options = new HashMap<String, Object>();
		options.put(XMIResource.OPTION_SCHEMA_LOCATION, Boolean.TRUE);
		res.save(options);
	}

	private static Program loadProgram() {
		PicoPackageImpl.init();
		ResourceSet rs = new ResourceSetImpl();
		Resource res = rs
				.getResource(URI.createFileURI("model/prog.xmi"), true);
		return (Program) res.getContents().get(0);
	}

	private static Program createProgram() {
		Program prog = PicoFactory.eINSTANCE.createProgram();
		Declaration decl = PicoFactory.eINSTANCE.createDeclaration();
		decl.setId("a");
		decl.setType("natural");
		prog.getDeclarations().add(decl);
		decl = PicoFactory.eINSTANCE.createDeclaration();
		decl.setId("toto");
		decl.setType("string");
		prog.getDeclarations().add(decl);
		return prog;
	}
}
